/**
 * Copyright (C), 2001-2031, www.bosssoft.com.cn
 * FileName: UserContextHandlerInterceptor.java
 * Author: Camera
 * Date: 2024/8/5 11:27
 * Description:
 * <p>
 * History:
 * Date          Author   Version  Desc
 * 2024-01-01    bosssoft  1.0.0   initialize this file
 */
package com.bosssoft.common.token;


import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
/**
 * @className: UserContextHandlerInterceptor
 * @description:
 * @author: Camera
 * @date: 2024/8/511:27
 * @since 1.0
 **/
@Component(value = "userContextHandlerInterceptor")
@Slf4j
public class UserContextHandlerInterceptor extends HandlerInterceptorAdapter {
    /**
     *  取得app common包的版本兼容配置信息
     */
    @Resource
    private AppConfiguration appConfiguration;

    public  static final String ACCESS_TOKEN = "accessToken";

    /**
     * 解密次数
     */
    public static final int DECODE_COUNT = 2;

    /**
     * 解密
     * @param token 加密后的token
     * @return 解密后的token
     */
    private String getDecodeToken(String token) {
        String result = token;
        for(int i = 0;i < DECODE_COUNT;i++){
            final byte[] textByte = result.getBytes(StandardCharsets.UTF_8);
            result =  new String(Base64.getDecoder().decode(textByte), StandardCharsets.UTF_8);
        }
        return result;
    }

    /**
     * 预处理请求的拦截器方法，用于在请求处理之前执行一些操作。
     * 这个方法主要负责从请求中提取用户信息，并将其存储到上下文中，以便后续使用。
     *
     * @param request 当前的 HttpServletRequest 对象，包含请求信息。
     * @param response 当前的 HttpServletResponse 对象，用于构造响应。
     * @param handler 当前请求映射的处理器，或其子类型的实例。
     * @return 如果预处理成功返回 true，否则返回 false。
     * @throws Exception 可能抛出的异常。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 如果是从登录页面
            if ("doPostAccessToken".equals(handlerMethod.getMethod().getName())) {
                return super.preHandle(request, response, handler);
            }

            //从 Header 中获取用户信息
            String userString = request.getHeader(BaseContextConstants.USER_CONTEXT_ATTRIBUTES);
            if (StringUtil.isNotEmpty(userString)) {
                JSONObject userJsonObject = new JSONObject(userString);

                // 将 用户 ID，用户名，机构 ID，公司 ID，权限集合 存入 BaseContextHolder，方便后续使用
                BaseContextHolder.setUserId(Convert.toLong(userJsonObject.get(BaseContextConstants.JWT_KEY_USER_ID)));
                BaseContextHolder.set(BaseContextConstants.JWT_KEY_USER_NAME,
                        Convert.toLong(userJsonObject.get(BaseContextConstants.JWT_KEY_USER_NAME)));
                // 这里 要用到 tanentId、orgId、companyId等
                BaseContextHolder.setTanentId(Convert.toLong(userJsonObject.get(BaseContextConstants.JWT_KEY_TANENT_ID)));
                BaseContextHolder.setOrgId(Convert.toLong(userJsonObject.get(BaseContextConstants.JWT_KEY_ORG_ID)));
                BaseContextHolder.setCompanyId(Convert.toLong(userJsonObject.get(BaseContextConstants.JWT_KEY_COMPANY_ID)));
                BaseContextHolder.setAuthorities(userJsonObject.getStr(BaseContextConstants.JWT_KEY_AUTHORITIES));
            }
            String token = getToken(request);
            //往BaseContext中设置了token，account与roleIds的列表
            setTokenToBaseContext(token);
        }
        return super.preHandle(request, response, handler);
    }

    /**
     * 完成请求处理后的拦截器方法，用于清理或收尾工作。
     * 此方法在请求处理流程结束之后执行，无论是正常结束还是异常结束。
     *
     * @param request 当前的 HttpServletRequest 对象，包含请求信息。
     * @param response 当前的 HttpServletResponse 对象，用于构造响应。
     * @param handler 处理请求的处理器，或其子类型的实例。
     * @param exception 可能发生的异常，如果没有异常则为 null。
     * @throws Exception 可能抛出的异常。
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
        BaseContextHolder.remove();
        super.afterCompletion(request, response, handler, exception);
    }

    /**
     * 从 HttpServletRequest 中获取并解码访问令牌（Token）。
     * 访问令牌通常用于身份验证和授权，此方法尝试从请求的 Header 和 Query Parameter 中获取它。
     * 如果找到令牌，它将被解码以获取原始的访问令牌字符串。
     *
     * @param request 当前的 HttpServletRequest 对象，包含请求信息。
     * @return 返回获取并解码后的访问令牌，如果未找到或解码失败则返回 null 或空字符串。
     */
    public String getToken(HttpServletRequest request) {
        String token = request.getHeader(ACCESS_TOKEN);
        if (StringUtil.isEmpty(token)) {
            token = request.getParameter(ACCESS_TOKEN);
        }
        // 解密token
        if(!StringUtil.isEmpty(token)){
            token = getDecodeToken(token);
        }
        return token;
    }

    /**
     * 将token解析放到BaseContextHolder
     *
     * @param token  从header取得的token
     */
    private void setTokenToBaseContext(String token) {
        if (StringUtil.isNotEmpty(token)) {
            BaseContextHolder.setToken(token);
            List<String> jwtTokenList = new ArrayList<>();
            if(appConfiguration.isCommonCompatibleEnable()){
                // 旧格式因为token加密需要解析
                jwtTokenList = TokenUtil.decryptToken(token);
            }else {
                // 修改 TokenUtil.decryptToken为 parseToken2List
                jwtTokenList = TokenUtil.parseToken2List(token);
            }

            //token的head中的信息
            if (!jwtTokenList.get(0).isEmpty()) {
                com.alibaba.fastjson.JSONObject object1 = JSON.parseObject(jwtTokenList.get(0));
                object1.getString("alg");
                object1.getString("typ");
            }
            //token中payload中的信息
            if (!jwtTokenList.get(1).isEmpty()) {
                com.alibaba.fastjson.JSONObject object1 = JSON.parseObject(jwtTokenList.get(1));
                final String account = object1.getString("account");
                final String roleIdArrayString = object1.getString("roleIds");
                final String companyId = object1.getString("companyId");
                final String tenantId = object1.getString("tenantId");
                final String userName = object1.getString("name");
                final String userId = object1.getString("userId");
                final String orgId = object1.getString("organizationId");
                List<Object> roleIdList = JSON.parseArray(roleIdArrayString, Object.class);
                BaseContextHolder.set(account, roleIdList);
                BaseContextHolder.setCompanyId(Long.parseLong(companyId));
                BaseContextHolder.setTanentId(Long.parseLong(tenantId));
                BaseContextHolder.setOrgId(Long.parseLong(orgId));
                BaseContextHolder.setLoginUserId(Long.parseLong(userId));
                BaseContextHolder.setLoginUserName(userName);
            }
        }
    }
}
